
//
// 110109 - AH - (Hector) Created.
// 110110a- AH - (Garage *) Java doc todo done.
// 110110b- AH - (Garage *) getLast().
// 110110c- AH - (Garage *) addTail(RetrievalEvent).
// 110110d- AH - (Garage *) getTime().
// 110110e- AH - (Garage *) getSource().
// 110111a- AH - EMPTY_ARRAY.
// 110111 - AH - getPath().
// 110116 - AH - (Bien) setIsPaintEvent() and getIsPaintEvent().
// 110119 - AH - (Hector) Fixed getPath().
// 110130 - AH - (Bien) turn property.
// 110606 - AH - (Garage) RetrievalEvent(String, RetrievalEventType) and
//               seqToString().
//

package org.soekrat.engine.wheel.retrieval;

import java.io.CharArrayWriter;
import java.io.PrintWriter;
import java.util.Stack;

import org.soekrat.AttributeObject;

/**
 * <p>
 *   Represent an event of a retrieval strategy unfolding.
 * </p>
 * @author Arne Halvorsen (AH) 
 * @author Laetitia Le Chatton (LLC)
 */
public final class RetrievalEvent extends AttributeObject 
{
  private final long m_time=System.currentTimeMillis();
  
  private final String m_desc;
  
  private RetrievalEvent m_prev=null;
  
  private RetrievalEvent m_next=null;
  
  private final RetrievalEventSource m_source;
  
  private final RetrievalEventType m_type;
  
  private int m_turn=-1;
  
  /**
   * <p>
   *   Creates event with no description, no source and of type 
   *   {@link RetrievalEventType#STEP}.
   * </p>
   */
  public RetrievalEvent(){ this(null, RetrievalEventType.STEP, null); }
  
  /**
   * <p>
   *   Creates event with no source and of type 
   *   {@link RetrievalEventType#STEP}.
   * </p>
   * @param desc Description of event, may be {@code null}.
   */
  public RetrievalEvent(String desc)
  { 
    this(desc, RetrievalEventType.STEP, null); 
  }
  
  /**
   * <p>
   *   Creates event with no source.
   * </p>
   * @param desc Description of event, may be {@code null}.
   * @param type Type of event.
   */
  public RetrievalEvent(String desc, RetrievalEventType type)
  { 
    this(desc, type, null); 
  }
  
  /**
   * <p>
   *   Constructor.
   * </p>
   * @param desc   Description of event, may be {@code null}.
   * @param type   Type of event.
   * @param source Identifies the source of the event. Is allowed to be 
   *               {@code null} (source unknown).
   */
  public RetrievalEvent(String desc, RetrievalEventType type, 
    RetrievalEventSource source)
  {
    desc=(desc==null) ? "" : desc.trim();
    
    if (type==null)
    {
      throw new NullPointerException("type");
    }
    
    m_prev=null;
    m_desc=desc;
    m_source=source;
    m_type=type;
  }
  
  /**
   * <p>
   *   Constructor.
   * </p>
   * @param prev   Previous event to {@code this}.
   * @param desc   Description of event, may be {@code null}.
   * @param type   Type of event. 
   * @param source Identifies the source of the event. Is allowed to be 
   *               {@code null} (source unknown). 
   */
  public RetrievalEvent(RetrievalEvent prev, String desc, 
    RetrievalEventType type, RetrievalEventSource source)
  {
    if (prev==null)
    {
      throw new NullPointerException("prev");
    }
    
    if (type==null)
    {
      throw new NullPointerException("type");
    }
    
    desc=(desc==null) ? "" : desc.trim();
    
    m_prev=prev;
    m_prev.m_next=this;
    m_type=type;
    m_desc=desc;
    m_source=source;
  }
  
  /**
   * <p>
   *   Adds sequence {@code tail} is first event to the end of the sequence
   *   {@code this} is in.
   * </p>
   * @param tail First event of tail.
   * @throws IllegalArgumentException If {@code tail} is not first in a 
   *         sequence.
   */
  public void addTail(RetrievalEvent tail)
  {
    if (tail==null)
    {
      throw new NullPointerException("tail");
    }
    
    if (tail.m_prev!=null)
    {
      throw new IllegalArgumentException("tail is not head of a sequence");
    }
    
    RetrievalEvent last=getLast();
    last.m_next=tail;
    tail.m_prev=this;
  }
  
  /**
   * <p>
   *   Gets previous event.
   * </p>
   * @return The previous event in sequence, {@code null} if is first.
   */
  public RetrievalEvent getPrevious(){ return m_prev; }
  
  /**
   * <p>
   *   Gets next event.
   * </p>
   * @return Next event in sequence, {@code null} if is last.
   */
  public RetrievalEvent getNext(){ return m_next; }
  
  /**
   * <p>
   *   Gets description.
   * </p>
   * @return Description.
   */
  public String getDescription(){ return m_desc; }
  
  /**
   * <p>
   *   Gets last event in sequence {@code this} is in.
   * </p>
   * @return Last event.
   */
  public RetrievalEvent getLast()
  { 
    return m_next==null ? this : m_next.getLast();
  }
  
  @Override
  public String toString()
  {
    StringBuilder sb=new StringBuilder();
    
    boolean first=true;
    
    if (m_source!=null) 
    {
      sb.append(m_source);
      first=false;
    }
    
    if (!first) sb.append(", ");
    
    sb.append("Type: ").append(m_type);
    
    if (m_desc.length()>0)
    {
      sb.append(" : ").append(m_desc);
    }
    
    return sb.toString(); 
  }
  
  /**
   * <p>
   *   Gets
   *   {@code String} presentation of the sequence of events starting from
   *   {@code this}.
   * </p>
   * @return Compiled.
   */
  public String seqToString()
  {
    CharArrayWriter caw=new CharArrayWriter();
    PrintWriter pw=new PrintWriter(caw);
    
    RetrievalEvent curr=this;
    while (curr!=null)
    {
      pw.println(curr);
      curr=curr.m_next;
    }
    
    pw.flush();
    return caw.toString();
  }
  
  /**
   * <p>
   *   Gets a
   *   {@link String} presentation of the event sequence starting with 
   *   {@code this}.
   * </p>
   * @return {@link String} compiled.
   */
  public String sequenceToString()
  {
    return m_next==null ? m_desc : m_desc+" : "+m_next.sequenceToString();
  }
  
  /**
   * <p>
   *   Gets time stamp of event.
   * </p>
   * @return Time as produced by
   *         {@link System#currentTimeMillis()}.
   */
  public long getTime(){ return m_time; }
  
  /**
   * <p>
   *   Gets event type.
   * </p>
   * @return Type.
   */
  public RetrievalEventType getType(){ return m_type; }
  
  /**
   * <p>
   *   Gets object that identifies source of event.
   * </p>
   * @return {@link RetrievalEvent} or {@code null} source is unknown.
   */
  public RetrievalEventSource getSource(){ return m_source; }
  
  /**
   * <p>
   *   Gets path to {@code this}.
   * </p>
   * @return Path.
   */
  public Object[] getPath()
  {
    Stack<RetrievalEvent> s=new Stack<RetrievalEvent>();
    RetrievalEvent curr=this;
    while (curr!=null)
    {
      s.push(curr);
      curr=curr.m_prev;
    }
    
    int n=s.size();
    Object[] retVal=new Object[n];
    int i=0;
    while (!s.isEmpty()) retVal[i++]=s.pop();
    return retVal;
  }
  
  /**
   * <p>
   *   Sets turn {@code this} is for.
   * </p>
   * @param t Turn.
   * @return {@code this}.
   */
  public RetrievalEvent setTurn(int t)
  {
    if (t<0)
    {
      throw new IllegalArgumentException("t<0 : "+t);
    }
    
    m_turn=t;
    return this;
  }
  
  /**
   * <p>
   *   Gets turn {@code this} belong to if known.
   * </p>
   * @return Turn or {@code -1} if not known.
   */
  public int getTurn(){ return m_turn; }
  
  /**
   * <p>
   *   The empty array shared.
   * </p>
   */
  public static final RetrievalEvent[] EMPTY_ARRAY=new RetrievalEvent[0];
  
}
